Scenario 1: Random Scatter
To begin, pursue the point of view that structure in the data is indicated by departures from a uniform scatter of palindromes across the DNA.
Of course, a random uniform scatter does that mean that palindromes will be equally spaced as milestones on a freeway. There will be some gaps on the DNA where no palindromes occur, and there will be some clumping together of palindromes.
To look for structure examine the locations of the palindromes, the spacing between palindromes, and the counts of palindromes in non overlapping regions of the DNA. One starting place might be to see first how random scatter looks by using a computer to simulate it.
A computer can simulate 296 palindrome sites chosen at random along a DNA sequence of 229,354 bases using a pseudo random number generator. When this is done several times, by making seller sets of simulated palindrome locations, then the real data can be compared to the simulated data.
set.seed(0)
color <- 'red'
# Generate 3 samples from the uniform distribution with the same size and bounds as our data.
samples = list(sort(runif(n, min=0, max=N)), sort(runif(n, min=0, max=N)), sort(runif(n, min=0, max=N)))
# Dot plot of locations of palindromes in original data and uniform scatter.
title1 <- 'Locations of Palindromes'
title2 <- c(title1,'(Simulated)')
x.axis <- 'Base Pair'
symbol <- 3
stripchart(locations, pch=symbol, col=color, main=title1, xlab=x.axis)

for (sample in samples) {
stripchart(sample, pch=symbol, main=title2, xlab=x.axis)
}



# Additional dot plot of locations of palindromes in original data and uniform scatter.
dotchart(locations, color=color, main=title1, xlab=x.axis)

for (sample in samples) {
dotchart(sample, main=title2, xlab=x.axis)
}



# Histogram of locations of palindromes in original data and uniform scatter.
bins <- 35
hist(locations, col=color, breaks=bins, main=title1, xlab=x.axis)

for (sample in samples) {
hist(sample, breaks=bins, main=title2, xlab=x.axis)
}



# Scatterplot of spacing between consecutive palindromes
title1 <- 'Spacing between Consecutive Palindromes'
title2 <- c(title1,'(Simulated)')
x.axis <- 'Base Pair Location'
y.axis <- 'Distance (Base Pairs) from Previous Palindrome'
y.range <- c(0,5000)
plot(locations[-1], diff(locations), col=color, ylim=y.range, main=title1, xlab=x.axis, ylab=y.axis)

for (sample in samples) {
plot(sample[-1], diff(sample), ylim=y.range, main=title2, xlab=x.axis, ylab=y.axis)
}



# Histogram of counts of palindromes in non-overlapping regions in original data and uniform scatter.
interval.length <- 2500
title1 <- paste('Number of Palindromes in Non-Overlapping Regions of Length', interval.length)
title2 <- c(title1,'(Simulated)')
x.axis <- 'Number of Palindromes'
bins <- seq(0,20,1)
hist(as.vector(table(cut(locations, breaks=seq(0,N,interval.length), include.lowest = TRUE))), breaks=bins, col=color, main=title1, xlab=x.axis)

for (sample in samples) {
hist(as.vector(table(cut(sample, breaks=seq(0,N,interval.length), include.lowest = TRUE))), breaks=bins, main=title2, xlab=x.axis)
}



Scenario 2: Locations and Spacings
Use graphical methods to examine the spacings between consecutive palindromes and sum of consecutive pairs, triplets, etc, spacings. Compare what you find to what you would expect to find in a random scatter. Also, use graphical methods to compare locations of the palindromes.
Locations We performed the tests three times over three different lengths of sub-intervals.
# Histogram of locations of palindromes in original data and uniform scatter
uniform <- sample.int(N, size = n)
hist(locations, breaks = 20, probability = TRUE, col = rgb(1,0,0,0.5), main = "Location Data Distribution Comparison", xlab= "Palindrome Locations")
lines(density(locations, adjust = 2), col = 2)
hist(uniform,breaks = 20, probability = TRUE, col = rgb(0,0,1,0.5), add = TRUE)
lines(density(uniform, adjust = 2), col = 4)
legend(x = 180000, y = 0.000008, legend = c("Sample", "Uniform"), lty = c(1,1), col = c(rgb(1,0,0,0.5), rgb(0,0,1,0.5)))

# Chi-square Goodness of Fit Test
# Case 1: k(number of sub-intervals) = 21
k <- 20
locations.expected <- n/k
tab <- table(cut(locations, breaks = seq(0, N, length.out = k+1), include.lowest = TRUE))
locations.observed <- as.vector(tab)
chi_2 <- sum((locations.observed - locations.expected)^2/locations.expected)
chi2_compare <- qchisq(p = 0.95, df = 19)
p_value <- pchisq(chi_2, df = 19, lower.tail = FALSE)
print(cat("\nWhen conducting chi_square Goodness of fit test comparing locations(divided in 20 sub-intervals) against uniform distribution\n"))
When conducting chi_square Goodness of fit test comparing locations(divided in 20 sub-intervals) against uniform distribution
NULL
print(paste("The value of chi_square statistic is", chi_2))
[1] "The value of chi_square statistic is 17.9189189189189"
print(paste("The p_value is", p_value))
[1] "The p_value is 0.527860332119311"
## Visualization of the Residual
Residuals <- (locations.observed - locations.expected) / sqrt(locations.expected)
plot(Residuals, type = 'h', ylab = "Standardized Residuals", xlab = "Palindrome locations", main = "Plot of Standardized Residual for Locations (divided in 20 sub-intervals)")

# Case 2: k(number of sub-intervals) = 100
k <- 30
locations.expected <- n/k
tab <- table(cut(locations, breaks = seq(0, N, length.out = k+1), include.lowest = TRUE))
locations.observed <- as.vector(tab)
chi_2 <- sum((locations.observed - locations.expected)^2/locations.expected)
chi2_compare <- qchisq(p = 0.95, df = 29)
p_value <- pchisq(chi_2, df = 29, lower.tail = FALSE)
print(cat("\nWhen conducting chi_square Goodness of fit test comparing locations(divided in 30 sub-intervals) against uniform distribution\n"))
When conducting chi_square Goodness of fit test comparing locations(divided in 30 sub-intervals) against uniform distribution
NULL
print(paste("The value of chi_square statistic is", chi_2))
[1] "The value of chi_square statistic is 40.6891891891892"
print(paste("The p_value is", p_value))
[1] "The p_value is 0.0732835870345071"
## Visualization of the Residual
Residuals <- (locations.observed - locations.expected) / sqrt(locations.expected)
plot(Residuals, type = 'h', ylab = "Standardized Residuals", xlab = "Palindrome locations", main = "Plot of Standardized Residual for Locations (divided in 30 sub-intervals)")

# Case 3: k(number of sub-intervals) = 500
k <- 60
locations.expected <- n/k
tab <- table(cut(locations, breaks = seq(0, N, length.out = k+1), include.lowest = TRUE))
locations.observed <- as.vector(tab)
chi_2 <- sum((locations.observed - locations.expected)^2/locations.expected)
chi2_compare <- qchisq(p = 0.95, df = 59)
p_value <- pchisq(chi_2, df = 59, lower.tail = FALSE)
print(cat("\nWhen conducting chi_square Goodness of fit test comparing locations(divided in 60 sub-intervals) against uniform distribution\n"))
When conducting chi_square Goodness of fit test comparing locations(divided in 60 sub-intervals) against uniform distribution
NULL
print(paste("The value of chi_square statistic is", chi_2))
[1] "The value of chi_square statistic is 79"
print(paste("The p_value is", p_value))
[1] "The p_value is 0.0421403871302519"
## Visualization of the Residual
Residuals <- (locations.observed - locations.expected) / sqrt(locations.expected)
plot(Residuals, type = 'h', ylab = "Standardized Residuals", xlab = "Palindrome locations", main = "Plot of Standardized Residual for Locations (divided in 60 sub-intervals)")

Inference: Null Hypothesis: Locations are distributed uniformly. Conclusion: Since p-value of this chi-square test is smaller than 0.05, we fail to reject the null hypothesis. It indicates that deviations as large as ours are not so likely. Hence, we conclude that it appears that uniform is not a reasonable initial model. Residual Conclusion: Since the value of the standardized residual is larger than 3, the probability model of a uniform distribution is lack of fit.
Spacings We performed the tests three times over three different lengths of sub-intervals.
# Consecutive Pairs
locations.sorted = sort(locations, decreasing = FALSE)
distance.pair <- abs(locations.sorted[-1]-locations.sorted[-length(locations.sorted)])
# Histogram of spacings of palindromes in original data and exponential distribution
hist(distance.pair, breaks= 15, col = rgb(1,0,0,0.5), probability = TRUE, main = "Consecutive Pairs Spacings Distribution Comparison", xlab = "Distance between Consecutive Palindromes Locations", ylim = c(0,0.001))
lines(density(distance.pair, adjust = 2), col = rgb(1,0,0,0.5))
Expo <- rexp(n-1, rate = 1/mean(distance.pair))
hist(Expo, breaks = 15, col = rgb(0,0,1,0.5), probability = TRUE, add = TRUE)
lines(density(Expo, adjust = 2), col = rgb(0,0,1,0.5))
legend(x = 4200, y = 0.0009, legend = c("Sample", "Exponential"), lty = c(1,1), col = c(rgb(1,0,0,0.5), rgb(0,0,1,0.5)))

# Chi-square Goodness of Fit Test
# Construct observed numbers of intervals
# Case 1: Divided in 7 intervals
# Construct expected number of intervals
spacings.observed <- sort(distance.pair, decreasing = FALSE)
lambda <- 1/mean(distance.pair)
spacings.intervals <- as.numeric(quantile(spacings.observed, probs = c(0,0.05, 0.1, 0.3, 0.5, 0.7, 0.9,1)))
spacings.expected <- (n-1)*(exp(-lambda*spacings.intervals[-length(spacings.intervals)])-exp(-lambda*spacings.intervals[-1]))
spacings.expected[length(spacings.expected)] <- n-sum(spacings.expected[1:length(spacings.expected)-1])
spacings.observed <- as.numeric(table(cut(distance.pair, breaks = spacings.intervals, include.lowest = TRUE)))
contingency_7 <- data.frame(spacings.intervals[-1],spacings.observed,spacings.expected)
contingency_7
chi_2 <- sum((spacings.observed - spacings.expected)^2/spacings.expected)
chi2_compare <- qchisq(p = 0.95, df = 5)
p_value <- pchisq(chi_2, df = 5, lower.tail = FALSE)
print(paste("The p_value when the distance is splited into 7 sub-intervals is", p_value))
[1] "The p_value when the distance is splited into 7 sub-intervals is 6.42447875347949e-05"
## Visualization of the Residual
Residuals <- (spacings.observed - spacings.expected) / sqrt(spacings.expected)
plot(Residuals, type = 'h', ylab = "Standardized Residuals", xlab = "Palindrome locations", main = "Plot of Standardized Residual for Locations (divided in 7 bins)")

# Case 2: Divided in 10 intervals
# Construct expected number of intervals
spacings.observed <- sort(distance.pair, decreasing = FALSE)
spacings.intervals <- as.numeric(quantile(spacings.observed, probs = c(seq(0,1, by = 0.1))))
spacings.expected <- (n-1)*(exp(-lambda*spacings.intervals[-length(spacings.intervals)])-exp(-lambda*spacings.intervals[-1]))
spacings.expected[length(spacings.expected)] <- n-sum(spacings.expected[1:length(spacings.expected)-1])
spacings.observed <- as.numeric(table(cut(distance.pair, breaks = spacings.intervals, include.lowest = TRUE)))
contingency_10 <- data.frame(spacings.intervals[-1],spacings.observed,spacings.expected)
contingency_10
chi_2 <- sum((spacings.observed - spacings.expected)^2/spacings.expected)
chi2_compare <- qchisq(p = 0.95, df = 8)
p_value <- pchisq(chi_2, df = 8, lower.tail = FALSE)
print(paste("The p_value when the distance is splited into 10 sub-intervals is", p_value))
[1] "The p_value when the distance is splited into 10 sub-intervals is 0.000218981752365422"
## Visualization of the Residual
Residuals <- (spacings.observed - spacings.expected) / sqrt(spacings.expected)
plot(Residuals, type = 'h', ylab = "Standardized Residuals", xlab = "Palindrome locations", main = "Plot of Standardized Residual for Locations (divided in 10 bins)")

# Case 3: Divided in 20 intervals
# Construct expected number of intervals
spacings.observed <- sort(distance.pair, decreasing = FALSE)
spacings.intervals <- as.numeric(quantile(spacings.observed, probs = c(seq(0,1, by = 0.05))))
spacings.expected <- (n-1)*(exp(-lambda*spacings.intervals[-length(spacings.intervals)])-exp(-lambda*spacings.intervals[-1]))
spacings.expected[length(spacings.expected)] <- n-sum(spacings.expected[1:length(spacings.expected)-1])
spacings.observed <- as.numeric(table(cut(distance.pair, breaks = spacings.intervals, include.lowest = TRUE)))
contingency_20 <- data.frame(spacings.intervals[-1],spacings.observed,spacings.expected)
contingency_20
chi_2 <- sum((spacings.observed - spacings.expected)^2/spacings.expected)
chi2_compare <- qchisq(p = 0.95, df = 18)
p_value <- pchisq(chi_2, df = 18, lower.tail = FALSE)
print(paste("The p_value when the distance is splited into 20 sub-intervals is", p_value))
[1] "The p_value when the distance is splited into 20 sub-intervals is 0.0117020696134169"
## Visualization of the Residual
Residuals <- (spacings.observed - spacings.expected) / sqrt(spacings.expected)
plot(Residuals, type = 'h', ylab = "Standardized Residuals", xlab = "Palindrome locations", main = "Plot of Standardized Residual for Locations (divided in 20 bins)")

# Consecutive Triplets
locations.sorted <- sort(locations, decreasing = FALSE)
locations.triplets <- locations.sorted[-length(locations.sorted)]
distance.triplets <- abs(locations.sorted[-1][-1]-locations.triplets[-length(locations.triplets)])
# Histogram of spacings of palindromes in original data and exponential distribution
hist(distance.triplets, breaks= 15, col = rgb(1,0,0,0.5), probability = TRUE, main = "Consecutive Triplets Spacings Distribution Comparison", xlab = "Distance between Consecutive Palindromes Locations", ylim = c(0,0.001))
lines(density(distance.triplets, adjust = 2), col = rgb(1,0,0,0.5))
Gam <- rgamma(n-2, 2, rate = 2/mean(distance.triplets))
hist(Gam, breaks = 15, col = rgb(0,0,1,0.5), probability = TRUE, add = TRUE)
lines(density(Gam, adjust = 2), col = rgb(0,0,1,0.5))
legend(x = 4200, y = 0.0005, legend = c("Sample", "Gamma"), lty = c(1,1), col = c(rgb(1,0,0,0.5), rgb(0,0,1,0.5)))

# Chi-square Goodness of Fit Test
# Construct observed numbers of intervals
# Case 1: Divided in 7 intervals
# Construct expected number of intervals
spacings.observed <- sort(distance.triplets, decreasing = FALSE)
lambda <- 2/mean(distance.pair)
spacings.intervals <- as.numeric(quantile(spacings.observed, probs = c(0,0.05, 0.1, 0.3, 0.5, 0.7, 0.9,1)))
spacings.expected <- (n-1)*(exp(-lambda*spacings.intervals[-length(spacings.intervals)])-exp(-lambda*spacings.intervals[-1]))
spacings.expected[length(spacings.expected)] <- n-sum(spacings.expected[1:length(spacings.expected)-1])
spacings.observed <- as.numeric(table(cut(distance.triplets, breaks = spacings.intervals, include.lowest = TRUE)))
contingency_7 <- data.frame(spacings.intervals[-1],spacings.observed,spacings.expected)
contingency_7
chi_2 <- sum((spacings.observed - spacings.expected)^2/spacings.expected)
chi2_compare <- qchisq(p = 0.95, df = 5)
p_value <- pchisq(chi_2, df = 5, lower.tail = FALSE)
print(paste("The p_value when the distance is splited into 7 sub-intervals is", p_value))
[1] "The p_value when the distance is splited into 7 sub-intervals is 0"
## Visualization of the Residual
Residuals <- (spacings.observed - spacings.expected) / sqrt(spacings.expected)
plot(Residuals, type = 'h', ylab = "Standardized Residuals", xlab = "Palindrome locations", main = "Plot of Standardized Residual for Locations (divided in 7 bins)")

# Case 2: Divided in 10 intervals
# Construct expected number of intervals
spacings.observed <- sort(distance.triplets, decreasing = FALSE)
spacings.intervals <- as.numeric(quantile(spacings.observed, probs = c(seq(0,1, by = 0.1))))
spacings.expected <- (n-1)*(exp(-lambda*spacings.intervals[-length(spacings.intervals)])-exp(-lambda*spacings.intervals[-1]))
spacings.expected[length(spacings.expected)] <- n-sum(spacings.expected[1:length(spacings.expected)-1])
spacings.observed <- as.numeric(table(cut(distance.triplets, breaks = spacings.intervals, include.lowest = TRUE)))
contingency_10 <- data.frame(spacings.intervals[-1],spacings.observed,spacings.expected)
contingency_10
chi_2 <- sum((spacings.observed - spacings.expected)^2/spacings.expected)
chi2_compare <- qchisq(p = 0.95, df = 8)
p_value <- pchisq(chi_2, df = 8, lower.tail = FALSE)
print(paste("The p_value when the distance is splited into 10 sub-intervals is", p_value))
[1] "The p_value when the distance is splited into 10 sub-intervals is 0"
## Visualization of the Residual
Residuals <- (spacings.observed - spacings.expected) / sqrt(spacings.expected)
plot(Residuals, type = 'h', ylab = "Standardized Residuals", xlab = "Palindrome locations", main = "Plot of Standardized Residual for Locations (divided in 10 bins)")

# Case 3: Divided in 20 intervals
# Construct expected number of intervals
spacings.observed <- sort(distance.triplets, decreasing = FALSE)
spacings.intervals <- as.numeric(quantile(spacings.observed, probs = c(seq(0,1, by = 0.05))))
spacings.expected <- (n-1)*(exp(-lambda*spacings.intervals[-length(spacings.intervals)])-exp(-lambda*spacings.intervals[-1]))
spacings.expected[length(spacings.expected)] <- n-sum(spacings.expected[1:length(spacings.expected)-1])
spacings.observed <- as.numeric(table(cut(distance.triplets, breaks = spacings.intervals, include.lowest = TRUE)))
contingency_20 <- data.frame(spacings.intervals[-1],spacings.observed,spacings.expected)
contingency_20
chi_2 <- sum((spacings.observed - spacings.expected)^2/spacings.expected)
chi2_compare <- qchisq(p = 0.95, df = 18)
p_value <- pchisq(chi_2, df = 18, lower.tail = FALSE)
print(paste("The p_value when the distance is splited into 20 sub-intervals is", p_value))
[1] "The p_value when the distance is splited into 20 sub-intervals is 0"
## Visualization of the Residual
Residuals <- (spacings.observed - spacings.expected) / sqrt(spacings.expected)
plot(Residuals, type = 'h', ylab = "Standardized Residuals", xlab = "Palindrome locations", main = "Plot of Standardized Residual for Locations (divided in 20 bins)")

Scenario 3: Counts
Use graphical methods and more formal statistical tests to examine the counts of palindromes in various regions of the DNA. Split the DNA into nonoverlapping regions of equal length to compare the number of palindomres in an interval to the number of that you would expect from uniform random scatter. The counts for shorter regions will be more variable than those for logner regions. Also, consider classifying the regions according to the number of counts.
k <- 50
tab <- table(cut(locations, breaks = seq(0, N, length.out = k+1), include.lowest = TRUE))
counts.obs <- as.vector(tab)
a <- table(cut(counts.obs, breaks = seq(min(counts.obs), max(counts.obs), length.out = k+1), include.lowest = TRUE))
# Histogram of counts of palindromes in original data and poisson distribution
hist(counts.obs, breaks = 15, col = rgb(1,0,0,0.5), probability = TRUE, main = "Counts Distribution Comparison (50 Sub-intervals)", xlab = "Number of Palindromes Sites Inside an Interval", ylim = c(0,0.2))
lines(density(counts.obs, adjust = 2), col = rgb(1,0,0,0.5))
Pois <- rpois(n, lambda = mean(counts.obs))
hist(Pois, breaks = 15, col = rgb(0,0,1,0.5), probability = TRUE, add = TRUE)
lines(density(Pois, adjust = 2), col = rgb(0,0,1,0.5))
legend(x = 12, y = 0.15, legend = c("sample", "Poisson"), lty = c(1,1), col = c(rgb(1,0,0,0.5), rgb(0,0,1,0.5)))

# Chi-Square Testing
# Construct palindrome count number
palindrome.count <- c("0,1,2", "3","4","5","6","7","8+")
# Construct observed number of intervals
intervals.observed <- c(8, 9, 13, 10, 8, 8, 5)
# Construct expected number of intervals
expected <- c()
lambda <- n/k
for (i in c(0:17)){
expect <- k* exp(-lambda)* lambda**i /factorial(i)
expected <- c(expected, expect)
}
sum <- 0
for (j in c(9:17)){
sum <- sum+expected[j]
}
intervals.expected <- c(expected[1]+expected[2]+expected[3],expected[4],expected[5],expected[6],expected[7],expected[8],sum)
# Create contingency table
b <- data.frame(palindrome.count,intervals.observed,intervals.expected)
b
chi_2 <- sum((intervals.observed - intervals.expected)^2/intervals.expected)
chi2_compare <- qchisq(p = 0.95, df = 7)
p_value <- pchisq(chi_2, df = 7, lower.tail = FALSE)
print(cat("\nWhen conducting chi_square Goodness of fit test comparing locations(divided in 500 sub-intervals) against uniform distribution\n"))
When conducting chi_square Goodness of fit test comparing locations(divided in 500 sub-intervals) against uniform distribution
NULL
print(paste("The value of chi_square statistic is", chi_2))
[1] "The value of chi_square statistic is 21.2720502400911"
print(paste("The p_value is", p_value))
[1] "The p_value is 0.00338769156571031"
LS0tCnRpdGxlOiAiQ0FTRSBTVFVEWSAzOiBTRUFSQ0ggRk9SIFRIRSBVTlVTVUFMIENMVVNURVIgSU4gVEhFIFBBTElORFJPTUVTIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgoKCiMjIFF1ZXN0aW9uCkluIHRoaXMgcGFwZXIsIHdlIHdpbGwgc2VhcmNoIGZvciB1bnVzdWFsIGNsdXN0ZXJzIG9mIGNvbXBsZW1lbnRhcnkgcGFsaW5kcm9tZXMuIFRoZSBvdmVyYXJjaGluZyByZXNlYXJjaCBxdWVzdGlvbiBpczog4oCcSG93IGRvIHdlIGZpbmQgY2x1c3RlcnMgb2YgcGFsaW5kcm9tZXM/IEhvdyBkbyB3ZSBkZXRlcm1pbmUgd2hldGhlciBhIGNsdXN0ZXIgaXMganVzdCBhIGNoYW5jZSBvY2N1cnJlbmNlIG9yIGEgcG90ZW50aWFsIHJlcGxpY2F0aW9uIHNpdGU/IEJhc2VkIG9uIG91ciBhbmFseXNpcywgd2Ugd2lsbCB0aGVuIHByb3ZpZGUgcmVjb21tZW5kYXRpb25zIHRvIGJpb2xvZ2lzdHMgd2hvIGFyZSBhYm91dCB0byBzdGFydCBleHBlcmltZW50YWxseSBzZWFyY2hpbmcgZm9yIHRoZSBvcmlnaW4gb2YgcmVwbGljYXRpb24uCgoKCiMjIFNldHVwCmBgYHtyfQpsb2NhdGlvbnMgPC0gcmVhZC50YWJsZSgnaGNtdi0yNWtnam4xLTFyZnJ0a2MudHh0JywgaGVhZGVyPVRSVUUpJGxvY2F0aW9uICAjIE9yaWdpbmFsCmhlYWx0aCA8LSByZWFkLmNzdignUkFXX0RBVEEtMml3Y3pubi0ya3IyeHcwLmNzdicsIGhlYWRlciA9IFRSVUUpICAjIEFkZGl0aW9uYWwKCk4gPC0gMjI5MzU0ICAjIEJhc2UgcGFpcnMKbiA8LSAyOTYgICMgUGFsaW5kcm9tZXMKYGBgCgoKCiMjIFNjZW5hcmlvIDE6IFJhbmRvbSBTY2F0dGVyClRvIGJlZ2luLCBwdXJzdWUgdGhlIHBvaW50IG9mIHZpZXcgdGhhdCBzdHJ1Y3R1cmUgaW4gdGhlIGRhdGEgaXMgaW5kaWNhdGVkIGJ5IGRlcGFydHVyZXMgZnJvbSBhIHVuaWZvcm0gc2NhdHRlciBvZiBwYWxpbmRyb21lcyBhY3Jvc3MgdGhlIEROQS4KCipPZiBjb3Vyc2UsIGEgcmFuZG9tIHVuaWZvcm0gc2NhdHRlciBkb2VzIHRoYXQgbWVhbiB0aGF0IHBhbGluZHJvbWVzIHdpbGwgYmUgZXF1YWxseSBzcGFjZWQgYXMgbWlsZXN0b25lcyBvbiBhIGZyZWV3YXkuIFRoZXJlIHdpbGwgYmUgc29tZSBnYXBzIG9uIHRoZSBETkEgd2hlcmUgbm8gcGFsaW5kcm9tZXMgb2NjdXIsIGFuZCB0aGVyZSB3aWxsIGJlIHNvbWUgY2x1bXBpbmcgdG9nZXRoZXIgb2YgcGFsaW5kcm9tZXMuKgoKVG8gbG9vayBmb3Igc3RydWN0dXJlIGV4YW1pbmUgdGhlIGxvY2F0aW9ucyBvZiB0aGUgcGFsaW5kcm9tZXMsIHRoZSBzcGFjaW5nIGJldHdlZW4gcGFsaW5kcm9tZXMsIGFuZCB0aGUgY291bnRzIG9mIHBhbGluZHJvbWVzIGluIG5vbiBvdmVybGFwcGluZyByZWdpb25zIG9mIHRoZSBETkEuIE9uZSBzdGFydGluZyBwbGFjZSBtaWdodCBiZSB0byBzZWUgZmlyc3QgaG93IHJhbmRvbSBzY2F0dGVyIGxvb2tzIGJ5IHVzaW5nIGEgY29tcHV0ZXIgdG8gc2ltdWxhdGUgaXQuCgoqQSBjb21wdXRlciBjYW4gc2ltdWxhdGUgMjk2IHBhbGluZHJvbWUgc2l0ZXMgY2hvc2VuIGF0IHJhbmRvbSBhbG9uZyBhIEROQSBzZXF1ZW5jZSBvZiAyMjksMzU0IGJhc2VzIHVzaW5nIGEgcHNldWRvIHJhbmRvbSBudW1iZXIgZ2VuZXJhdG9yLiBXaGVuIHRoaXMgaXMgZG9uZSBzZXZlcmFsIHRpbWVzLCBieSBtYWtpbmcgc2VsbGVyIHNldHMgb2Ygc2ltdWxhdGVkIHBhbGluZHJvbWUgbG9jYXRpb25zLCB0aGVuIHRoZSByZWFsIGRhdGEgY2FuIGJlIGNvbXBhcmVkIHRvIHRoZSBzaW11bGF0ZWQgZGF0YS4qCmBgYHtyfQpzZXQuc2VlZCgwKQpjb2xvciA8LSAncmVkJwoKIyBHZW5lcmF0ZSAzIHNhbXBsZXMgZnJvbSB0aGUgdW5pZm9ybSBkaXN0cmlidXRpb24gd2l0aCB0aGUgc2FtZSBzaXplIGFuZCBib3VuZHMgYXMgb3VyIGRhdGEuCnNhbXBsZXMgPSBsaXN0KHNvcnQocnVuaWYobiwgbWluPTAsIG1heD1OKSksIHNvcnQocnVuaWYobiwgbWluPTAsIG1heD1OKSksIHNvcnQocnVuaWYobiwgbWluPTAsIG1heD1OKSkpCgojIERvdCBwbG90IG9mIGxvY2F0aW9ucyBvZiBwYWxpbmRyb21lcyBpbiBvcmlnaW5hbCBkYXRhIGFuZCB1bmlmb3JtIHNjYXR0ZXIuCnRpdGxlMSA8LSAnTG9jYXRpb25zIG9mIFBhbGluZHJvbWVzJwp0aXRsZTIgPC0gYyh0aXRsZTEsJyhTaW11bGF0ZWQpJykKeC5heGlzIDwtICdCYXNlIFBhaXInCnN5bWJvbCA8LSAzCnN0cmlwY2hhcnQobG9jYXRpb25zLCBwY2g9c3ltYm9sLCBjb2w9Y29sb3IsIG1haW49dGl0bGUxLCB4bGFiPXguYXhpcykKZm9yIChzYW1wbGUgaW4gc2FtcGxlcykgewogIHN0cmlwY2hhcnQoc2FtcGxlLCBwY2g9c3ltYm9sLCBtYWluPXRpdGxlMiwgeGxhYj14LmF4aXMpCn0KCiMgQWRkaXRpb25hbCBkb3QgcGxvdCBvZiBsb2NhdGlvbnMgb2YgcGFsaW5kcm9tZXMgaW4gb3JpZ2luYWwgZGF0YSBhbmQgdW5pZm9ybSBzY2F0dGVyLgpkb3RjaGFydChsb2NhdGlvbnMsIGNvbG9yPWNvbG9yLCBtYWluPXRpdGxlMSwgeGxhYj14LmF4aXMpCmZvciAoc2FtcGxlIGluIHNhbXBsZXMpIHsKICBkb3RjaGFydChzYW1wbGUsIG1haW49dGl0bGUyLCB4bGFiPXguYXhpcykKfQoKIyBIaXN0b2dyYW0gb2YgbG9jYXRpb25zIG9mIHBhbGluZHJvbWVzIGluIG9yaWdpbmFsIGRhdGEgYW5kIHVuaWZvcm0gc2NhdHRlci4KYmlucyA8LSAzNQpoaXN0KGxvY2F0aW9ucywgY29sPWNvbG9yLCBicmVha3M9YmlucywgbWFpbj10aXRsZTEsIHhsYWI9eC5heGlzKQpmb3IgKHNhbXBsZSBpbiBzYW1wbGVzKSB7CiAgaGlzdChzYW1wbGUsIGJyZWFrcz1iaW5zLCBtYWluPXRpdGxlMiwgeGxhYj14LmF4aXMpCn0KCgoKIyBTY2F0dGVycGxvdCBvZiBzcGFjaW5nIGJldHdlZW4gY29uc2VjdXRpdmUgcGFsaW5kcm9tZXMKdGl0bGUxIDwtICdTcGFjaW5nIGJldHdlZW4gQ29uc2VjdXRpdmUgUGFsaW5kcm9tZXMnCnRpdGxlMiA8LSBjKHRpdGxlMSwnKFNpbXVsYXRlZCknKQp4LmF4aXMgPC0gJ0Jhc2UgUGFpciBMb2NhdGlvbicKeS5heGlzIDwtICdEaXN0YW5jZSAoQmFzZSBQYWlycykgZnJvbSBQcmV2aW91cyBQYWxpbmRyb21lJwp5LnJhbmdlIDwtIGMoMCw1MDAwKQpwbG90KGxvY2F0aW9uc1stMV0sIGRpZmYobG9jYXRpb25zKSwgY29sPWNvbG9yLCB5bGltPXkucmFuZ2UsIG1haW49dGl0bGUxLCB4bGFiPXguYXhpcywgeWxhYj15LmF4aXMpCmZvciAoc2FtcGxlIGluIHNhbXBsZXMpIHsKICBwbG90KHNhbXBsZVstMV0sIGRpZmYoc2FtcGxlKSwgeWxpbT15LnJhbmdlLCBtYWluPXRpdGxlMiwgeGxhYj14LmF4aXMsIHlsYWI9eS5heGlzKQp9CgoKCiMgSGlzdG9ncmFtIG9mIGNvdW50cyBvZiBwYWxpbmRyb21lcyBpbiBub24tb3ZlcmxhcHBpbmcgcmVnaW9ucyBpbiBvcmlnaW5hbCBkYXRhIGFuZCB1bmlmb3JtIHNjYXR0ZXIuCmludGVydmFsLmxlbmd0aCA8LSAyNTAwCnRpdGxlMSA8LSBwYXN0ZSgnTnVtYmVyIG9mIFBhbGluZHJvbWVzIGluIE5vbi1PdmVybGFwcGluZyBSZWdpb25zIG9mIExlbmd0aCcsIGludGVydmFsLmxlbmd0aCkKdGl0bGUyIDwtIGModGl0bGUxLCcoU2ltdWxhdGVkKScpCnguYXhpcyA8LSAnTnVtYmVyIG9mIFBhbGluZHJvbWVzJwpiaW5zIDwtIHNlcSgwLDIwLDEpCmhpc3QoYXMudmVjdG9yKHRhYmxlKGN1dChsb2NhdGlvbnMsIGJyZWFrcz1zZXEoMCxOLGludGVydmFsLmxlbmd0aCksIGluY2x1ZGUubG93ZXN0ID0gVFJVRSkpKSwgYnJlYWtzPWJpbnMsIGNvbD1jb2xvciwgbWFpbj10aXRsZTEsIHhsYWI9eC5heGlzKQpmb3IgKHNhbXBsZSBpbiBzYW1wbGVzKSB7CiAgaGlzdChhcy52ZWN0b3IodGFibGUoY3V0KHNhbXBsZSwgYnJlYWtzPXNlcSgwLE4saW50ZXJ2YWwubGVuZ3RoKSwgaW5jbHVkZS5sb3dlc3QgPSBUUlVFKSkpLCBicmVha3M9YmlucywgbWFpbj10aXRsZTIsIHhsYWI9eC5heGlzKQp9CmBgYAoKCgojIyBTY2VuYXJpbyAyOiBMb2NhdGlvbnMgYW5kIFNwYWNpbmdzClVzZSBncmFwaGljYWwgbWV0aG9kcyB0byBleGFtaW5lIHRoZSBzcGFjaW5ncyBiZXR3ZWVuIGNvbnNlY3V0aXZlIHBhbGluZHJvbWVzIGFuZCBzdW0gb2YgY29uc2VjdXRpdmUgcGFpcnMsIHRyaXBsZXRzLCBldGMsIHNwYWNpbmdzLiBDb21wYXJlIHdoYXQgeW91IGZpbmQgdG8gd2hhdCB5b3Ugd291bGQgZXhwZWN0IHRvIGZpbmQgaW4gYSByYW5kb20gc2NhdHRlci4gQWxzbywgdXNlIGdyYXBoaWNhbCBtZXRob2RzIHRvIGNvbXBhcmUgbG9jYXRpb25zIG9mIHRoZSBwYWxpbmRyb21lcy4KCkxvY2F0aW9ucwpXZSBwZXJmb3JtZWQgdGhlIHRlc3RzIHRocmVlIHRpbWVzIG92ZXIgdGhyZWUgZGlmZmVyZW50IGxlbmd0aHMgb2Ygc3ViLWludGVydmFscy4KYGBge3J9CiMgSGlzdG9ncmFtIG9mIGxvY2F0aW9ucyBvZiBwYWxpbmRyb21lcyBpbiBvcmlnaW5hbCBkYXRhIGFuZCB1bmlmb3JtIHNjYXR0ZXIKdW5pZm9ybSA8LSBzYW1wbGUuaW50KE4sIHNpemUgPSBuKQpoaXN0KGxvY2F0aW9ucywgYnJlYWtzID0gMjAsIHByb2JhYmlsaXR5ID0gVFJVRSwgY29sID0gcmdiKDEsMCwwLDAuNSksIG1haW4gPSAiTG9jYXRpb24gRGF0YSBEaXN0cmlidXRpb24gQ29tcGFyaXNvbiIsIHhsYWI9ICJQYWxpbmRyb21lIExvY2F0aW9ucyIpCmxpbmVzKGRlbnNpdHkobG9jYXRpb25zLCBhZGp1c3QgPSAyKSwgY29sID0gMikKaGlzdCh1bmlmb3JtLGJyZWFrcyA9IDIwLCBwcm9iYWJpbGl0eSA9IFRSVUUsIGNvbCA9IHJnYigwLDAsMSwwLjUpLCBhZGQgPSBUUlVFKQpsaW5lcyhkZW5zaXR5KHVuaWZvcm0sIGFkanVzdCA9IDIpLCBjb2wgPSA0KQpsZWdlbmQoeCA9IDE4MDAwMCwgeSA9IDAuMDAwMDA4LCBsZWdlbmQgPSBjKCJTYW1wbGUiLCAiVW5pZm9ybSIpLCBsdHkgPSBjKDEsMSksIGNvbCA9IGMocmdiKDEsMCwwLDAuNSksIHJnYigwLDAsMSwwLjUpKSkKCiMgQ2hpLXNxdWFyZSBHb29kbmVzcyBvZiBGaXQgVGVzdAojIENhc2UgMTogayhudW1iZXIgb2Ygc3ViLWludGVydmFscykgPSAyMAprIDwtIDIwCmxvY2F0aW9ucy5leHBlY3RlZCA8LSBuL2sKdGFiIDwtIHRhYmxlKGN1dChsb2NhdGlvbnMsIGJyZWFrcyA9IHNlcSgwLCBOLCBsZW5ndGgub3V0ID0gaysxKSwgaW5jbHVkZS5sb3dlc3QgPSBUUlVFKSkKbG9jYXRpb25zLm9ic2VydmVkIDwtIGFzLnZlY3Rvcih0YWIpCmNoaV8yIDwtIHN1bSgobG9jYXRpb25zLm9ic2VydmVkIC0gbG9jYXRpb25zLmV4cGVjdGVkKV4yL2xvY2F0aW9ucy5leHBlY3RlZCkKY2hpMl9jb21wYXJlIDwtIHFjaGlzcShwID0gMC45NSwgZGYgPSAxOSkKcF92YWx1ZSA8LSBwY2hpc3EoY2hpXzIsIGRmID0gMTksIGxvd2VyLnRhaWwgPSBGQUxTRSkKcHJpbnQoY2F0KCJcbldoZW4gY29uZHVjdGluZyBjaGlfc3F1YXJlIEdvb2RuZXNzIG9mIGZpdCB0ZXN0IGNvbXBhcmluZyBsb2NhdGlvbnMoZGl2aWRlZCBpbiAyMCBzdWItaW50ZXJ2YWxzKSBhZ2FpbnN0IHVuaWZvcm0gZGlzdHJpYnV0aW9uXG4iKSkKcHJpbnQocGFzdGUoIlRoZSB2YWx1ZSBvZiBjaGlfc3F1YXJlIHN0YXRpc3RpYyBpcyIsIGNoaV8yKSkKcHJpbnQocGFzdGUoIlRoZSBwX3ZhbHVlIGlzIiwgcF92YWx1ZSkpCgojIyBWaXN1YWxpemF0aW9uIG9mIHRoZSBSZXNpZHVhbApSZXNpZHVhbHMgPC0gKGxvY2F0aW9ucy5vYnNlcnZlZCAtIGxvY2F0aW9ucy5leHBlY3RlZCkgLyBzcXJ0KGxvY2F0aW9ucy5leHBlY3RlZCkKcGxvdChSZXNpZHVhbHMsIHR5cGUgPSAnaCcsIHlsYWIgPSAiU3RhbmRhcmRpemVkIFJlc2lkdWFscyIsIHhsYWIgPSAiUGFsaW5kcm9tZSBsb2NhdGlvbnMiLCBtYWluID0gIlBsb3Qgb2YgU3RhbmRhcmRpemVkIFJlc2lkdWFsIGZvciBMb2NhdGlvbnMgKGRpdmlkZWQgaW4gMjAgc3ViLWludGVydmFscykiKQoKIyBDYXNlIDI6IGsobnVtYmVyIG9mIHN1Yi1pbnRlcnZhbHMpID0gMzAKayA8LSAzMApsb2NhdGlvbnMuZXhwZWN0ZWQgPC0gbi9rCnRhYiA8LSB0YWJsZShjdXQobG9jYXRpb25zLCBicmVha3MgPSBzZXEoMCwgTiwgbGVuZ3RoLm91dCA9IGsrMSksIGluY2x1ZGUubG93ZXN0ID0gVFJVRSkpCmxvY2F0aW9ucy5vYnNlcnZlZCA8LSBhcy52ZWN0b3IodGFiKQpjaGlfMiA8LSBzdW0oKGxvY2F0aW9ucy5vYnNlcnZlZCAtIGxvY2F0aW9ucy5leHBlY3RlZCleMi9sb2NhdGlvbnMuZXhwZWN0ZWQpCmNoaTJfY29tcGFyZSA8LSBxY2hpc3EocCA9IDAuOTUsIGRmID0gMjkpCnBfdmFsdWUgPC0gcGNoaXNxKGNoaV8yLCBkZiA9IDI5LCBsb3dlci50YWlsID0gRkFMU0UpCnByaW50KGNhdCgiXG5XaGVuIGNvbmR1Y3RpbmcgY2hpX3NxdWFyZSBHb29kbmVzcyBvZiBmaXQgdGVzdCBjb21wYXJpbmcgbG9jYXRpb25zKGRpdmlkZWQgaW4gMzAgc3ViLWludGVydmFscykgYWdhaW5zdCB1bmlmb3JtIGRpc3RyaWJ1dGlvblxuIikpCnByaW50KHBhc3RlKCJUaGUgdmFsdWUgb2YgY2hpX3NxdWFyZSBzdGF0aXN0aWMgaXMiLCBjaGlfMikpCnByaW50KHBhc3RlKCJUaGUgcF92YWx1ZSBpcyIsIHBfdmFsdWUpKQoKIyMgVmlzdWFsaXphdGlvbiBvZiB0aGUgUmVzaWR1YWwKUmVzaWR1YWxzIDwtIChsb2NhdGlvbnMub2JzZXJ2ZWQgLSBsb2NhdGlvbnMuZXhwZWN0ZWQpIC8gc3FydChsb2NhdGlvbnMuZXhwZWN0ZWQpCnBsb3QoUmVzaWR1YWxzLCB0eXBlID0gJ2gnLCB5bGFiID0gIlN0YW5kYXJkaXplZCBSZXNpZHVhbHMiLCB4bGFiID0gIlBhbGluZHJvbWUgbG9jYXRpb25zIiwgbWFpbiA9ICJQbG90IG9mIFN0YW5kYXJkaXplZCBSZXNpZHVhbCBmb3IgTG9jYXRpb25zIChkaXZpZGVkIGluIDMwIHN1Yi1pbnRlcnZhbHMpIikKCiMgQ2FzZSAzOiBrKG51bWJlciBvZiBzdWItaW50ZXJ2YWxzKSA9IDYwCmsgPC0gNjAKbG9jYXRpb25zLmV4cGVjdGVkIDwtIG4vawp0YWIgPC0gdGFibGUoY3V0KGxvY2F0aW9ucywgYnJlYWtzID0gc2VxKDAsIE4sIGxlbmd0aC5vdXQgPSBrKzEpLCBpbmNsdWRlLmxvd2VzdCA9IFRSVUUpKQpsb2NhdGlvbnMub2JzZXJ2ZWQgPC0gYXMudmVjdG9yKHRhYikKY2hpXzIgPC0gc3VtKChsb2NhdGlvbnMub2JzZXJ2ZWQgLSBsb2NhdGlvbnMuZXhwZWN0ZWQpXjIvbG9jYXRpb25zLmV4cGVjdGVkKQpjaGkyX2NvbXBhcmUgPC0gcWNoaXNxKHAgPSAwLjk1LCBkZiA9IDU5KQpwX3ZhbHVlIDwtIHBjaGlzcShjaGlfMiwgZGYgPSA1OSwgbG93ZXIudGFpbCA9IEZBTFNFKQpwcmludChjYXQoIlxuV2hlbiBjb25kdWN0aW5nIGNoaV9zcXVhcmUgR29vZG5lc3Mgb2YgZml0IHRlc3QgY29tcGFyaW5nIGxvY2F0aW9ucyhkaXZpZGVkIGluIDYwIHN1Yi1pbnRlcnZhbHMpIGFnYWluc3QgdW5pZm9ybSBkaXN0cmlidXRpb25cbiIpKQpwcmludChwYXN0ZSgiVGhlIHZhbHVlIG9mIGNoaV9zcXVhcmUgc3RhdGlzdGljIGlzIiwgY2hpXzIpKQpwcmludChwYXN0ZSgiVGhlIHBfdmFsdWUgaXMiLCBwX3ZhbHVlKSkKCiMjIFZpc3VhbGl6YXRpb24gb2YgdGhlIFJlc2lkdWFsClJlc2lkdWFscyA8LSAobG9jYXRpb25zLm9ic2VydmVkIC0gbG9jYXRpb25zLmV4cGVjdGVkKSAvIHNxcnQobG9jYXRpb25zLmV4cGVjdGVkKQpwbG90KFJlc2lkdWFscywgdHlwZSA9ICdoJywgeWxhYiA9ICJTdGFuZGFyZGl6ZWQgUmVzaWR1YWxzIiwgeGxhYiA9ICJQYWxpbmRyb21lIGxvY2F0aW9ucyIsIG1haW4gPSAiUGxvdCBvZiBTdGFuZGFyZGl6ZWQgUmVzaWR1YWwgZm9yIExvY2F0aW9ucyAoZGl2aWRlZCBpbiA2MCBzdWItaW50ZXJ2YWxzKSIpCmBgYApJbmZlcmVuY2U6Ck51bGwgSHlwb3RoZXNpczogTG9jYXRpb25zIGFyZSBkaXN0cmlidXRlZCB1bmlmb3JtbHkuCkNvbmNsdXNpb246IFNpbmNlIHAtdmFsdWUgb2YgdGhpcyBjaGktc3F1YXJlIHRlc3QgaXMgc21hbGxlciB0aGFuIDAuMDUsIHdlIGZhaWwgdG8gcmVqZWN0IHRoZSBudWxsIGh5cG90aGVzaXMuIEl0IGluZGljYXRlcyB0aGF0IGRldmlhdGlvbnMgYXMgbGFyZ2UgYXMgb3VycyBhcmUgbm90IHNvIGxpa2VseS4gSGVuY2UsIHdlIGNvbmNsdWRlIHRoYXQgaXQgYXBwZWFycyB0aGF0IHVuaWZvcm0gaXMgbm90IGEgcmVhc29uYWJsZSBpbml0aWFsIG1vZGVsLiAKUmVzaWR1YWwgQ29uY2x1c2lvbjoKU2luY2UgdGhlIHZhbHVlIG9mIHRoZSBzdGFuZGFyZGl6ZWQgcmVzaWR1YWwgaXMgbGFyZ2VyIHRoYW4gMywgdGhlIHByb2JhYmlsaXR5IG1vZGVsIG9mIGEgdW5pZm9ybSBkaXN0cmlidXRpb24gaXMgbGFjayBvZiBmaXQuCgpTcGFjaW5ncwpXZSBwZXJmb3JtZWQgdGhlIHRlc3RzIHRocmVlIHRpbWVzIG92ZXIgdGhyZWUgZGlmZmVyZW50IGxlbmd0aHMgb2Ygc3ViLWludGVydmFscy4KYGBge3J9CiMgQ29uc2VjdXRpdmUgUGFpcnMKbG9jYXRpb25zLnNvcnRlZCA9IHNvcnQobG9jYXRpb25zLCBkZWNyZWFzaW5nID0gRkFMU0UpCmRpc3RhbmNlLnBhaXIgPC0gYWJzKGxvY2F0aW9ucy5zb3J0ZWRbLTFdLWxvY2F0aW9ucy5zb3J0ZWRbLWxlbmd0aChsb2NhdGlvbnMuc29ydGVkKV0pCgojIEhpc3RvZ3JhbSBvZiBzcGFjaW5ncyBvZiBwYWxpbmRyb21lcyBpbiBvcmlnaW5hbCBkYXRhIGFuZCBleHBvbmVudGlhbCBkaXN0cmlidXRpb24KaGlzdChkaXN0YW5jZS5wYWlyLCBicmVha3M9IDE1LCBjb2wgPSByZ2IoMSwwLDAsMC41KSwgcHJvYmFiaWxpdHkgPSBUUlVFLCBtYWluID0gIkNvbnNlY3V0aXZlIFBhaXJzIFNwYWNpbmdzIERpc3RyaWJ1dGlvbiBDb21wYXJpc29uIiwgeGxhYiA9ICJEaXN0YW5jZSBiZXR3ZWVuIENvbnNlY3V0aXZlIFBhbGluZHJvbWVzIExvY2F0aW9ucyIsIHlsaW0gPSBjKDAsMC4wMDEpKQpsaW5lcyhkZW5zaXR5KGRpc3RhbmNlLnBhaXIsIGFkanVzdCA9IDIpLCBjb2wgPSByZ2IoMSwwLDAsMC41KSkKRXhwbyA8LSByZXhwKG4tMSwgcmF0ZSA9IDEvbWVhbihkaXN0YW5jZS5wYWlyKSkKaGlzdChFeHBvLCBicmVha3MgPSAxNSwgY29sID0gcmdiKDAsMCwxLDAuNSksIHByb2JhYmlsaXR5ID0gVFJVRSwgYWRkID0gVFJVRSkKbGluZXMoZGVuc2l0eShFeHBvLCBhZGp1c3QgPSAyKSwgY29sID0gcmdiKDAsMCwxLDAuNSkpCmxlZ2VuZCh4ID0gNDIwMCwgeSA9IDAuMDAwOSwgbGVnZW5kID0gYygiU2FtcGxlIiwgIkV4cG9uZW50aWFsIiksIGx0eSA9IGMoMSwxKSwgY29sID0gYyhyZ2IoMSwwLDAsMC41KSwgcmdiKDAsMCwxLDAuNSkpKQoKIyBDaGktc3F1YXJlIEdvb2RuZXNzIG9mIEZpdCBUZXN0CiMgQ29uc3RydWN0IG9ic2VydmVkIG51bWJlcnMgb2YgaW50ZXJ2YWxzCgojIENhc2UgMTogRGl2aWRlZCBpbiA3IGludGVydmFscwojIENvbnN0cnVjdCBleHBlY3RlZCBudW1iZXIgb2YgaW50ZXJ2YWxzCnNwYWNpbmdzLm9ic2VydmVkIDwtIHNvcnQoZGlzdGFuY2UucGFpciwgZGVjcmVhc2luZyA9IEZBTFNFKQpsYW1iZGEgPC0gMS9tZWFuKGRpc3RhbmNlLnBhaXIpCnNwYWNpbmdzLmludGVydmFscyA8LSBhcy5udW1lcmljKHF1YW50aWxlKHNwYWNpbmdzLm9ic2VydmVkLCBwcm9icyA9IGMoMCwwLjA1LCAwLjEsIDAuMywgMC41LCAwLjcsIDAuOSwxKSkpCnNwYWNpbmdzLmV4cGVjdGVkIDwtIChuLTEpKihleHAoLWxhbWJkYSpzcGFjaW5ncy5pbnRlcnZhbHNbLWxlbmd0aChzcGFjaW5ncy5pbnRlcnZhbHMpXSktZXhwKC1sYW1iZGEqc3BhY2luZ3MuaW50ZXJ2YWxzWy0xXSkpCnNwYWNpbmdzLmV4cGVjdGVkW2xlbmd0aChzcGFjaW5ncy5leHBlY3RlZCldIDwtIG4tc3VtKHNwYWNpbmdzLmV4cGVjdGVkWzE6bGVuZ3RoKHNwYWNpbmdzLmV4cGVjdGVkKS0xXSkKc3BhY2luZ3Mub2JzZXJ2ZWQgPC0gYXMubnVtZXJpYyh0YWJsZShjdXQoZGlzdGFuY2UucGFpciwgYnJlYWtzID0gc3BhY2luZ3MuaW50ZXJ2YWxzLCBpbmNsdWRlLmxvd2VzdCA9IFRSVUUpKSkKY29udGluZ2VuY3lfNyA8LSBkYXRhLmZyYW1lKHNwYWNpbmdzLmludGVydmFsc1stMV0sc3BhY2luZ3Mub2JzZXJ2ZWQsc3BhY2luZ3MuZXhwZWN0ZWQpCmNvbnRpbmdlbmN5XzcKCmNoaV8yIDwtIHN1bSgoc3BhY2luZ3Mub2JzZXJ2ZWQgLSBzcGFjaW5ncy5leHBlY3RlZCleMi9zcGFjaW5ncy5leHBlY3RlZCkKY2hpMl9jb21wYXJlIDwtIHFjaGlzcShwID0gMC45NSwgZGYgPSA1KQpwX3ZhbHVlIDwtIHBjaGlzcShjaGlfMiwgZGYgPSA1LCBsb3dlci50YWlsID0gRkFMU0UpCnByaW50KHBhc3RlKCJUaGUgcF92YWx1ZSB3aGVuIHRoZSBkaXN0YW5jZSBpcyBzcGxpdGVkIGludG8gNyBzdWItaW50ZXJ2YWxzIGlzIiwgcF92YWx1ZSkpCiMjIFZpc3VhbGl6YXRpb24gb2YgdGhlIFJlc2lkdWFsClJlc2lkdWFscyA8LSAoc3BhY2luZ3Mub2JzZXJ2ZWQgLSBzcGFjaW5ncy5leHBlY3RlZCkgLyBzcXJ0KHNwYWNpbmdzLmV4cGVjdGVkKQpwbG90KFJlc2lkdWFscywgdHlwZSA9ICdoJywgeWxhYiA9ICJTdGFuZGFyZGl6ZWQgUmVzaWR1YWxzIiwgeGxhYiA9ICJQYWxpbmRyb21lIGxvY2F0aW9ucyIsIG1haW4gPSAiUGxvdCBvZiBTdGFuZGFyZGl6ZWQgUmVzaWR1YWwgZm9yIExvY2F0aW9ucyAoZGl2aWRlZCBpbiA3IGJpbnMpIikKCiMgQ2FzZSAyOiBEaXZpZGVkIGluIDEwIGludGVydmFscwojIENvbnN0cnVjdCBleHBlY3RlZCBudW1iZXIgb2YgaW50ZXJ2YWxzCnNwYWNpbmdzLm9ic2VydmVkIDwtIHNvcnQoZGlzdGFuY2UucGFpciwgZGVjcmVhc2luZyA9IEZBTFNFKQpzcGFjaW5ncy5pbnRlcnZhbHMgPC0gYXMubnVtZXJpYyhxdWFudGlsZShzcGFjaW5ncy5vYnNlcnZlZCwgcHJvYnMgPSBjKHNlcSgwLDEsIGJ5ID0gMC4xKSkpKQpzcGFjaW5ncy5leHBlY3RlZCA8LSAobi0xKSooZXhwKC1sYW1iZGEqc3BhY2luZ3MuaW50ZXJ2YWxzWy1sZW5ndGgoc3BhY2luZ3MuaW50ZXJ2YWxzKV0pLWV4cCgtbGFtYmRhKnNwYWNpbmdzLmludGVydmFsc1stMV0pKQpzcGFjaW5ncy5leHBlY3RlZFtsZW5ndGgoc3BhY2luZ3MuZXhwZWN0ZWQpXSA8LSBuLXN1bShzcGFjaW5ncy5leHBlY3RlZFsxOmxlbmd0aChzcGFjaW5ncy5leHBlY3RlZCktMV0pCnNwYWNpbmdzLm9ic2VydmVkIDwtIGFzLm51bWVyaWModGFibGUoY3V0KGRpc3RhbmNlLnBhaXIsIGJyZWFrcyA9IHNwYWNpbmdzLmludGVydmFscywgaW5jbHVkZS5sb3dlc3QgPSBUUlVFKSkpCmNvbnRpbmdlbmN5XzEwIDwtIGRhdGEuZnJhbWUoc3BhY2luZ3MuaW50ZXJ2YWxzWy0xXSxzcGFjaW5ncy5vYnNlcnZlZCxzcGFjaW5ncy5leHBlY3RlZCkKY29udGluZ2VuY3lfMTAKCmNoaV8yIDwtIHN1bSgoc3BhY2luZ3Mub2JzZXJ2ZWQgLSBzcGFjaW5ncy5leHBlY3RlZCleMi9zcGFjaW5ncy5leHBlY3RlZCkKY2hpMl9jb21wYXJlIDwtIHFjaGlzcShwID0gMC45NSwgZGYgPSA4KQpwX3ZhbHVlIDwtIHBjaGlzcShjaGlfMiwgZGYgPSA4LCBsb3dlci50YWlsID0gRkFMU0UpCnByaW50KHBhc3RlKCJUaGUgcF92YWx1ZSB3aGVuIHRoZSBkaXN0YW5jZSBpcyBzcGxpdGVkIGludG8gMTAgc3ViLWludGVydmFscyBpcyIsIHBfdmFsdWUpKQojIyBWaXN1YWxpemF0aW9uIG9mIHRoZSBSZXNpZHVhbApSZXNpZHVhbHMgPC0gKHNwYWNpbmdzLm9ic2VydmVkIC0gc3BhY2luZ3MuZXhwZWN0ZWQpIC8gc3FydChzcGFjaW5ncy5leHBlY3RlZCkKcGxvdChSZXNpZHVhbHMsIHR5cGUgPSAnaCcsIHlsYWIgPSAiU3RhbmRhcmRpemVkIFJlc2lkdWFscyIsIHhsYWIgPSAiUGFsaW5kcm9tZSBsb2NhdGlvbnMiLCBtYWluID0gIlBsb3Qgb2YgU3RhbmRhcmRpemVkIFJlc2lkdWFsIGZvciBMb2NhdGlvbnMgKGRpdmlkZWQgaW4gMTAgYmlucykiKQoKIyBDYXNlIDM6IERpdmlkZWQgaW4gMjAgaW50ZXJ2YWxzCiMgQ29uc3RydWN0IGV4cGVjdGVkIG51bWJlciBvZiBpbnRlcnZhbHMKc3BhY2luZ3Mub2JzZXJ2ZWQgPC0gc29ydChkaXN0YW5jZS5wYWlyLCBkZWNyZWFzaW5nID0gRkFMU0UpCnNwYWNpbmdzLmludGVydmFscyA8LSBhcy5udW1lcmljKHF1YW50aWxlKHNwYWNpbmdzLm9ic2VydmVkLCBwcm9icyA9IGMoc2VxKDAsMSwgYnkgPSAwLjA1KSkpKQpzcGFjaW5ncy5leHBlY3RlZCA8LSAobi0xKSooZXhwKC1sYW1iZGEqc3BhY2luZ3MuaW50ZXJ2YWxzWy1sZW5ndGgoc3BhY2luZ3MuaW50ZXJ2YWxzKV0pLWV4cCgtbGFtYmRhKnNwYWNpbmdzLmludGVydmFsc1stMV0pKQpzcGFjaW5ncy5leHBlY3RlZFtsZW5ndGgoc3BhY2luZ3MuZXhwZWN0ZWQpXSA8LSBuLXN1bShzcGFjaW5ncy5leHBlY3RlZFsxOmxlbmd0aChzcGFjaW5ncy5leHBlY3RlZCktMV0pCnNwYWNpbmdzLm9ic2VydmVkIDwtIGFzLm51bWVyaWModGFibGUoY3V0KGRpc3RhbmNlLnBhaXIsIGJyZWFrcyA9IHNwYWNpbmdzLmludGVydmFscywgaW5jbHVkZS5sb3dlc3QgPSBUUlVFKSkpCmNvbnRpbmdlbmN5XzIwIDwtIGRhdGEuZnJhbWUoc3BhY2luZ3MuaW50ZXJ2YWxzWy0xXSxzcGFjaW5ncy5vYnNlcnZlZCxzcGFjaW5ncy5leHBlY3RlZCkKY29udGluZ2VuY3lfMjAKCmNoaV8yIDwtIHN1bSgoc3BhY2luZ3Mub2JzZXJ2ZWQgLSBzcGFjaW5ncy5leHBlY3RlZCleMi9zcGFjaW5ncy5leHBlY3RlZCkKY2hpMl9jb21wYXJlIDwtIHFjaGlzcShwID0gMC45NSwgZGYgPSAxOCkKcF92YWx1ZSA8LSBwY2hpc3EoY2hpXzIsIGRmID0gMTgsIGxvd2VyLnRhaWwgPSBGQUxTRSkKcHJpbnQocGFzdGUoIlRoZSBwX3ZhbHVlIHdoZW4gdGhlIGRpc3RhbmNlIGlzIHNwbGl0ZWQgaW50byAyMCBzdWItaW50ZXJ2YWxzIGlzIiwgcF92YWx1ZSkpCiMjIFZpc3VhbGl6YXRpb24gb2YgdGhlIFJlc2lkdWFsClJlc2lkdWFscyA8LSAoc3BhY2luZ3Mub2JzZXJ2ZWQgLSBzcGFjaW5ncy5leHBlY3RlZCkgLyBzcXJ0KHNwYWNpbmdzLmV4cGVjdGVkKQpwbG90KFJlc2lkdWFscywgdHlwZSA9ICdoJywgeWxhYiA9ICJTdGFuZGFyZGl6ZWQgUmVzaWR1YWxzIiwgeGxhYiA9ICJQYWxpbmRyb21lIGxvY2F0aW9ucyIsIG1haW4gPSAiUGxvdCBvZiBTdGFuZGFyZGl6ZWQgUmVzaWR1YWwgZm9yIExvY2F0aW9ucyAoZGl2aWRlZCBpbiAyMCBiaW5zKSIpCmBgYApgYGB7cn0KIyBDb25zZWN1dGl2ZSBUcmlwbGV0cwpsb2NhdGlvbnMuc29ydGVkIDwtICBzb3J0KGxvY2F0aW9ucywgZGVjcmVhc2luZyA9IEZBTFNFKQpsb2NhdGlvbnMudHJpcGxldHMgPC0gbG9jYXRpb25zLnNvcnRlZFstbGVuZ3RoKGxvY2F0aW9ucy5zb3J0ZWQpXQpkaXN0YW5jZS50cmlwbGV0cyA8LSBhYnMobG9jYXRpb25zLnNvcnRlZFstMV1bLTFdLWxvY2F0aW9ucy50cmlwbGV0c1stbGVuZ3RoKGxvY2F0aW9ucy50cmlwbGV0cyldKQoKIyBIaXN0b2dyYW0gb2Ygc3BhY2luZ3Mgb2YgcGFsaW5kcm9tZXMgaW4gb3JpZ2luYWwgZGF0YSBhbmQgZXhwb25lbnRpYWwgZGlzdHJpYnV0aW9uCmhpc3QoZGlzdGFuY2UudHJpcGxldHMsIGJyZWFrcz0gMTUsIGNvbCA9IHJnYigxLDAsMCwwLjUpLCBwcm9iYWJpbGl0eSA9IFRSVUUsIG1haW4gPSAiQ29uc2VjdXRpdmUgVHJpcGxldHMgU3BhY2luZ3MgRGlzdHJpYnV0aW9uIENvbXBhcmlzb24iLCB4bGFiID0gIkRpc3RhbmNlIGJldHdlZW4gQ29uc2VjdXRpdmUgUGFsaW5kcm9tZXMgTG9jYXRpb25zIiwgeWxpbSA9IGMoMCwwLjAwMSkpCmxpbmVzKGRlbnNpdHkoZGlzdGFuY2UudHJpcGxldHMsIGFkanVzdCA9IDIpLCBjb2wgPSByZ2IoMSwwLDAsMC41KSkKR2FtIDwtIHJnYW1tYShuLTIsIDIsIHJhdGUgPSAyL21lYW4oZGlzdGFuY2UudHJpcGxldHMpKQpoaXN0KEdhbSwgYnJlYWtzID0gMTUsIGNvbCA9IHJnYigwLDAsMSwwLjUpLCBwcm9iYWJpbGl0eSA9IFRSVUUsIGFkZCA9IFRSVUUpCmxpbmVzKGRlbnNpdHkoR2FtLCBhZGp1c3QgPSAyKSwgY29sID0gcmdiKDAsMCwxLDAuNSkpCmxlZ2VuZCh4ID0gNDIwMCwgeSA9IDAuMDAwNSwgbGVnZW5kID0gYygiU2FtcGxlIiwgIkdhbW1hIiksIGx0eSA9IGMoMSwxKSwgY29sID0gYyhyZ2IoMSwwLDAsMC41KSwgcmdiKDAsMCwxLDAuNSkpKQoKIyBDaGktc3F1YXJlIEdvb2RuZXNzIG9mIEZpdCBUZXN0CiMgQ29uc3RydWN0IG9ic2VydmVkIG51bWJlcnMgb2YgaW50ZXJ2YWxzCgojIENhc2UgMTogRGl2aWRlZCBpbiA3IGludGVydmFscwojIENvbnN0cnVjdCBleHBlY3RlZCBudW1iZXIgb2YgaW50ZXJ2YWxzCnNwYWNpbmdzLm9ic2VydmVkIDwtIHNvcnQoZGlzdGFuY2UudHJpcGxldHMsIGRlY3JlYXNpbmcgPSBGQUxTRSkKbGFtYmRhIDwtIDIvbWVhbihkaXN0YW5jZS5wYWlyKQpzcGFjaW5ncy5pbnRlcnZhbHMgPC0gYXMubnVtZXJpYyhxdWFudGlsZShzcGFjaW5ncy5vYnNlcnZlZCwgcHJvYnMgPSBjKDAsMC4wNSwgMC4xLCAwLjMsIDAuNSwgMC43LCAwLjksMSkpKQpzcGFjaW5ncy5leHBlY3RlZCA8LSAobi0xKSooZXhwKC1sYW1iZGEqc3BhY2luZ3MuaW50ZXJ2YWxzWy1sZW5ndGgoc3BhY2luZ3MuaW50ZXJ2YWxzKV0pLWV4cCgtbGFtYmRhKnNwYWNpbmdzLmludGVydmFsc1stMV0pKQpzcGFjaW5ncy5leHBlY3RlZFtsZW5ndGgoc3BhY2luZ3MuZXhwZWN0ZWQpXSA8LSBuLXN1bShzcGFjaW5ncy5leHBlY3RlZFsxOmxlbmd0aChzcGFjaW5ncy5leHBlY3RlZCktMV0pCnNwYWNpbmdzLm9ic2VydmVkIDwtIGFzLm51bWVyaWModGFibGUoY3V0KGRpc3RhbmNlLnRyaXBsZXRzLCBicmVha3MgPSBzcGFjaW5ncy5pbnRlcnZhbHMsIGluY2x1ZGUubG93ZXN0ID0gVFJVRSkpKQpjb250aW5nZW5jeV83IDwtIGRhdGEuZnJhbWUoc3BhY2luZ3MuaW50ZXJ2YWxzWy0xXSxzcGFjaW5ncy5vYnNlcnZlZCxzcGFjaW5ncy5leHBlY3RlZCkKY29udGluZ2VuY3lfNwoKY2hpXzIgPC0gc3VtKChzcGFjaW5ncy5vYnNlcnZlZCAtIHNwYWNpbmdzLmV4cGVjdGVkKV4yL3NwYWNpbmdzLmV4cGVjdGVkKQpjaGkyX2NvbXBhcmUgPC0gcWNoaXNxKHAgPSAwLjk1LCBkZiA9IDUpCnBfdmFsdWUgPC0gcGNoaXNxKGNoaV8yLCBkZiA9IDUsIGxvd2VyLnRhaWwgPSBGQUxTRSkKcHJpbnQocGFzdGUoIlRoZSBwX3ZhbHVlIHdoZW4gdGhlIGRpc3RhbmNlIGlzIHNwbGl0ZWQgaW50byA3IHN1Yi1pbnRlcnZhbHMgaXMiLCBwX3ZhbHVlKSkKIyMgVmlzdWFsaXphdGlvbiBvZiB0aGUgUmVzaWR1YWwKUmVzaWR1YWxzIDwtIChzcGFjaW5ncy5vYnNlcnZlZCAtIHNwYWNpbmdzLmV4cGVjdGVkKSAvIHNxcnQoc3BhY2luZ3MuZXhwZWN0ZWQpCnBsb3QoUmVzaWR1YWxzLCB0eXBlID0gJ2gnLCB5bGFiID0gIlN0YW5kYXJkaXplZCBSZXNpZHVhbHMiLCB4bGFiID0gIlBhbGluZHJvbWUgbG9jYXRpb25zIiwgbWFpbiA9ICJQbG90IG9mIFN0YW5kYXJkaXplZCBSZXNpZHVhbCBmb3IgTG9jYXRpb25zIChkaXZpZGVkIGluIDcgYmlucykiKQoKIyBDYXNlIDI6IERpdmlkZWQgaW4gMTAgaW50ZXJ2YWxzCiMgQ29uc3RydWN0IGV4cGVjdGVkIG51bWJlciBvZiBpbnRlcnZhbHMKc3BhY2luZ3Mub2JzZXJ2ZWQgPC0gc29ydChkaXN0YW5jZS50cmlwbGV0cywgZGVjcmVhc2luZyA9IEZBTFNFKQpzcGFjaW5ncy5pbnRlcnZhbHMgPC0gYXMubnVtZXJpYyhxdWFudGlsZShzcGFjaW5ncy5vYnNlcnZlZCwgcHJvYnMgPSBjKHNlcSgwLDEsIGJ5ID0gMC4xKSkpKQpzcGFjaW5ncy5leHBlY3RlZCA8LSAobi0xKSooZXhwKC1sYW1iZGEqc3BhY2luZ3MuaW50ZXJ2YWxzWy1sZW5ndGgoc3BhY2luZ3MuaW50ZXJ2YWxzKV0pLWV4cCgtbGFtYmRhKnNwYWNpbmdzLmludGVydmFsc1stMV0pKQpzcGFjaW5ncy5leHBlY3RlZFtsZW5ndGgoc3BhY2luZ3MuZXhwZWN0ZWQpXSA8LSBuLXN1bShzcGFjaW5ncy5leHBlY3RlZFsxOmxlbmd0aChzcGFjaW5ncy5leHBlY3RlZCktMV0pCnNwYWNpbmdzLm9ic2VydmVkIDwtIGFzLm51bWVyaWModGFibGUoY3V0KGRpc3RhbmNlLnRyaXBsZXRzLCBicmVha3MgPSBzcGFjaW5ncy5pbnRlcnZhbHMsIGluY2x1ZGUubG93ZXN0ID0gVFJVRSkpKQpjb250aW5nZW5jeV8xMCA8LSBkYXRhLmZyYW1lKHNwYWNpbmdzLmludGVydmFsc1stMV0sc3BhY2luZ3Mub2JzZXJ2ZWQsc3BhY2luZ3MuZXhwZWN0ZWQpCmNvbnRpbmdlbmN5XzEwCgpjaGlfMiA8LSBzdW0oKHNwYWNpbmdzLm9ic2VydmVkIC0gc3BhY2luZ3MuZXhwZWN0ZWQpXjIvc3BhY2luZ3MuZXhwZWN0ZWQpCmNoaTJfY29tcGFyZSA8LSBxY2hpc3EocCA9IDAuOTUsIGRmID0gOCkKcF92YWx1ZSA8LSBwY2hpc3EoY2hpXzIsIGRmID0gOCwgbG93ZXIudGFpbCA9IEZBTFNFKQpwcmludChwYXN0ZSgiVGhlIHBfdmFsdWUgd2hlbiB0aGUgZGlzdGFuY2UgaXMgc3BsaXRlZCBpbnRvIDEwIHN1Yi1pbnRlcnZhbHMgaXMiLCBwX3ZhbHVlKSkKIyMgVmlzdWFsaXphdGlvbiBvZiB0aGUgUmVzaWR1YWwKUmVzaWR1YWxzIDwtIChzcGFjaW5ncy5vYnNlcnZlZCAtIHNwYWNpbmdzLmV4cGVjdGVkKSAvIHNxcnQoc3BhY2luZ3MuZXhwZWN0ZWQpCnBsb3QoUmVzaWR1YWxzLCB0eXBlID0gJ2gnLCB5bGFiID0gIlN0YW5kYXJkaXplZCBSZXNpZHVhbHMiLCB4bGFiID0gIlBhbGluZHJvbWUgbG9jYXRpb25zIiwgbWFpbiA9ICJQbG90IG9mIFN0YW5kYXJkaXplZCBSZXNpZHVhbCBmb3IgTG9jYXRpb25zIChkaXZpZGVkIGluIDEwIGJpbnMpIikKCiMgQ2FzZSAzOiBEaXZpZGVkIGluIDIwIGludGVydmFscwojIENvbnN0cnVjdCBleHBlY3RlZCBudW1iZXIgb2YgaW50ZXJ2YWxzCnNwYWNpbmdzLm9ic2VydmVkIDwtIHNvcnQoZGlzdGFuY2UudHJpcGxldHMsIGRlY3JlYXNpbmcgPSBGQUxTRSkKc3BhY2luZ3MuaW50ZXJ2YWxzIDwtIGFzLm51bWVyaWMocXVhbnRpbGUoc3BhY2luZ3Mub2JzZXJ2ZWQsIHByb2JzID0gYyhzZXEoMCwxLCBieSA9IDAuMDUpKSkpCnNwYWNpbmdzLmV4cGVjdGVkIDwtIChuLTEpKihleHAoLWxhbWJkYSpzcGFjaW5ncy5pbnRlcnZhbHNbLWxlbmd0aChzcGFjaW5ncy5pbnRlcnZhbHMpXSktZXhwKC1sYW1iZGEqc3BhY2luZ3MuaW50ZXJ2YWxzWy0xXSkpCnNwYWNpbmdzLmV4cGVjdGVkW2xlbmd0aChzcGFjaW5ncy5leHBlY3RlZCldIDwtIG4tc3VtKHNwYWNpbmdzLmV4cGVjdGVkWzE6bGVuZ3RoKHNwYWNpbmdzLmV4cGVjdGVkKS0xXSkKc3BhY2luZ3Mub2JzZXJ2ZWQgPC0gYXMubnVtZXJpYyh0YWJsZShjdXQoZGlzdGFuY2UudHJpcGxldHMsIGJyZWFrcyA9IHNwYWNpbmdzLmludGVydmFscywgaW5jbHVkZS5sb3dlc3QgPSBUUlVFKSkpCmNvbnRpbmdlbmN5XzIwIDwtIGRhdGEuZnJhbWUoc3BhY2luZ3MuaW50ZXJ2YWxzWy0xXSxzcGFjaW5ncy5vYnNlcnZlZCxzcGFjaW5ncy5leHBlY3RlZCkKY29udGluZ2VuY3lfMjAKCmNoaV8yIDwtIHN1bSgoc3BhY2luZ3Mub2JzZXJ2ZWQgLSBzcGFjaW5ncy5leHBlY3RlZCleMi9zcGFjaW5ncy5leHBlY3RlZCkKY2hpMl9jb21wYXJlIDwtIHFjaGlzcShwID0gMC45NSwgZGYgPSAxOCkKcF92YWx1ZSA8LSBwY2hpc3EoY2hpXzIsIGRmID0gMTgsIGxvd2VyLnRhaWwgPSBGQUxTRSkKcHJpbnQocGFzdGUoIlRoZSBwX3ZhbHVlIHdoZW4gdGhlIGRpc3RhbmNlIGlzIHNwbGl0ZWQgaW50byAyMCBzdWItaW50ZXJ2YWxzIGlzIiwgcF92YWx1ZSkpCiMjIFZpc3VhbGl6YXRpb24gb2YgdGhlIFJlc2lkdWFsClJlc2lkdWFscyA8LSAoc3BhY2luZ3Mub2JzZXJ2ZWQgLSBzcGFjaW5ncy5leHBlY3RlZCkgLyBzcXJ0KHNwYWNpbmdzLmV4cGVjdGVkKQpwbG90KFJlc2lkdWFscywgdHlwZSA9ICdoJywgeWxhYiA9ICJTdGFuZGFyZGl6ZWQgUmVzaWR1YWxzIiwgeGxhYiA9ICJQYWxpbmRyb21lIGxvY2F0aW9ucyIsIG1haW4gPSAiUGxvdCBvZiBTdGFuZGFyZGl6ZWQgUmVzaWR1YWwgZm9yIExvY2F0aW9ucyAoZGl2aWRlZCBpbiAyMCBiaW5zKSIpCgpgYGAKCgojIyBTY2VuYXJpbyAzOiBDb3VudHMKVXNlIGdyYXBoaWNhbCBtZXRob2RzIGFuZCBtb3JlIGZvcm1hbCBzdGF0aXN0aWNhbCB0ZXN0cyB0byBleGFtaW5lIHRoZSBjb3VudHMgb2YgcGFsaW5kcm9tZXMgaW4gdmFyaW91cyByZWdpb25zIG9mIHRoZSBETkEuIFNwbGl0IHRoZSBETkEgaW50byBub25vdmVybGFwcGluZyByZWdpb25zIG9mIGVxdWFsIGxlbmd0aCB0byBjb21wYXJlIHRoZSBudW1iZXIgb2YgcGFsaW5kb21yZXMgaW4gYW4gaW50ZXJ2YWwgdG8gdGhlIG51bWJlciBvZiB0aGF0IHlvdSB3b3VsZCBleHBlY3QgZnJvbSB1bmlmb3JtIHJhbmRvbSBzY2F0dGVyLiBUaGUgY291bnRzIGZvciBzaG9ydGVyIHJlZ2lvbnMgd2lsbCBiZSBtb3JlIHZhcmlhYmxlIHRoYW4gdGhvc2UgZm9yIGxvZ25lciByZWdpb25zLiBBbHNvLCBjb25zaWRlciBjbGFzc2lmeWluZyB0aGUgcmVnaW9ucyBhY2NvcmRpbmcgdG8gdGhlIG51bWJlciBvZiBjb3VudHMuCmBgYHtyfQprIDwtIDUwCnRhYiA8LSB0YWJsZShjdXQobG9jYXRpb25zLCBicmVha3MgPSBzZXEoMCwgTiwgbGVuZ3RoLm91dCA9IGsrMSksIGluY2x1ZGUubG93ZXN0ID0gVFJVRSkpCmNvdW50cy5vYnMgPC0gYXMudmVjdG9yKHRhYikKYSA8LSB0YWJsZShjdXQoY291bnRzLm9icywgYnJlYWtzID0gc2VxKG1pbihjb3VudHMub2JzKSwgbWF4KGNvdW50cy5vYnMpLCBsZW5ndGgub3V0ID0gaysxKSwgaW5jbHVkZS5sb3dlc3QgPSBUUlVFKSkKCiMgSGlzdG9ncmFtIG9mIGNvdW50cyBvZiBwYWxpbmRyb21lcyBpbiBvcmlnaW5hbCBkYXRhIGFuZCBwb2lzc29uIGRpc3RyaWJ1dGlvbgpoaXN0KGNvdW50cy5vYnMsIGJyZWFrcyA9IDE1LCBjb2wgPSByZ2IoMSwwLDAsMC41KSwgcHJvYmFiaWxpdHkgPSBUUlVFLCBtYWluID0gIkNvdW50cyBEaXN0cmlidXRpb24gQ29tcGFyaXNvbiAoNTAgU3ViLWludGVydmFscykiLCB4bGFiID0gIk51bWJlciBvZiBQYWxpbmRyb21lcyBTaXRlcyBJbnNpZGUgYW4gSW50ZXJ2YWwiLCB5bGltID0gYygwLDAuMikpCmxpbmVzKGRlbnNpdHkoY291bnRzLm9icywgYWRqdXN0ID0gMiksIGNvbCA9IHJnYigxLDAsMCwwLjUpKQpQb2lzIDwtIHJwb2lzKG4sIGxhbWJkYSA9IG1lYW4oY291bnRzLm9icykpCmhpc3QoUG9pcywgYnJlYWtzID0gMTUsIGNvbCA9IHJnYigwLDAsMSwwLjUpLCBwcm9iYWJpbGl0eSA9IFRSVUUsIGFkZCA9IFRSVUUpCmxpbmVzKGRlbnNpdHkoUG9pcywgYWRqdXN0ID0gMiksIGNvbCA9IHJnYigwLDAsMSwwLjUpKQpsZWdlbmQoeCA9IDEyLCB5ID0gMC4xNSwgbGVnZW5kID0gYygic2FtcGxlIiwgIlBvaXNzb24iKSwgbHR5ID0gYygxLDEpLCBjb2wgPSBjKHJnYigxLDAsMCwwLjUpLCByZ2IoMCwwLDEsMC41KSkpCgojIENoaS1TcXVhcmUgVGVzdGluZwojIENvbnN0cnVjdCBwYWxpbmRyb21lIGNvdW50IG51bWJlcgpwYWxpbmRyb21lLmNvdW50IDwtIGMoIjAsMSwyIiwgIjMiLCI0IiwiNSIsIjYiLCI3IiwiOCsiKQojIENvbnN0cnVjdCBvYnNlcnZlZCBudW1iZXIgb2YgaW50ZXJ2YWxzCmludGVydmFscy5vYnNlcnZlZCA8LSBjKDgsIDksIDEzLCAxMCwgOCwgOCwgNSkgCiMgQ29uc3RydWN0IGV4cGVjdGVkIG51bWJlciBvZiBpbnRlcnZhbHMKZXhwZWN0ZWQgPC0gYygpCmxhbWJkYSA8LSBuL2sKZm9yIChpIGluIGMoMDoxNykpewogIGV4cGVjdCA8LSBrKiBleHAoLWxhbWJkYSkqIGxhbWJkYSoqaSAvZmFjdG9yaWFsKGkpCiAgZXhwZWN0ZWQgPC0gYyhleHBlY3RlZCwgZXhwZWN0KQp9CnN1bSA8LSAwCmZvciAoaiBpbiBjKDk6MTcpKXsKICBzdW0gPC0gc3VtK2V4cGVjdGVkW2pdCn0KaW50ZXJ2YWxzLmV4cGVjdGVkIDwtIGMoZXhwZWN0ZWRbMV0rZXhwZWN0ZWRbMl0rZXhwZWN0ZWRbM10sZXhwZWN0ZWRbNF0sZXhwZWN0ZWRbNV0sZXhwZWN0ZWRbNl0sZXhwZWN0ZWRbN10sZXhwZWN0ZWRbOF0sc3VtKQoKIyBDcmVhdGUgY29udGluZ2VuY3kgdGFibGUKYiA8LSBkYXRhLmZyYW1lKHBhbGluZHJvbWUuY291bnQsaW50ZXJ2YWxzLm9ic2VydmVkLGludGVydmFscy5leHBlY3RlZCkKYgoKY2hpXzIgPC0gc3VtKChpbnRlcnZhbHMub2JzZXJ2ZWQgLSBpbnRlcnZhbHMuZXhwZWN0ZWQpXjIvaW50ZXJ2YWxzLmV4cGVjdGVkKQpjaGkyX2NvbXBhcmUgPC0gcWNoaXNxKHAgPSAwLjk1LCBkZiA9IDcpCnBfdmFsdWUgPC0gcGNoaXNxKGNoaV8yLCBkZiA9IDcsIGxvd2VyLnRhaWwgPSBGQUxTRSkKcHJpbnQoY2F0KCJcbldoZW4gY29uZHVjdGluZyBjaGlfc3F1YXJlIEdvb2RuZXNzIG9mIGZpdCB0ZXN0IGNvbXBhcmluZyBsb2NhdGlvbnMoZGl2aWRlZCBpbiA1MDAgc3ViLWludGVydmFscykgYWdhaW5zdCB1bmlmb3JtIGRpc3RyaWJ1dGlvblxuIikpCnByaW50KHBhc3RlKCJUaGUgdmFsdWUgb2YgY2hpX3NxdWFyZSBzdGF0aXN0aWMgaXMiLCBjaGlfMikpCnByaW50KHBhc3RlKCJUaGUgcF92YWx1ZSBpcyIsIHBfdmFsdWUpKQoKYGBgCgoKIyMgU2NlbmFyaW8gNDogVGhlIEJpZ2dlc3QgQ2x1c3RlcgpEb2VzIHRoZSBpbnRlcnZhbCB3aXRoIHRoZSBncmVhdGVzdCBudW1iZXIgb2YgcGFsaW5kcm9tZXMgaW5kaWNhdGUgYSBwb3RlbnRpYWwgb3JpZ2luIG9mIHJlcGxpY2F0aW9uPyBCZSBjYXJlZnVsIGluIG1ha2luZyB5b3VyIGludGVydmFscywgZm9yIGFueSBzbWFsbCwgYnV0IHNpZ25pZmljYW50IGRldmlhdGlvbnMgZnJvbSByYW5kb20gc2NhdHRlciwgc3VjaCBhcyBhIHRpZ2h0IGNsdXN0ZXIgb2YgYSBmZXcgcGFsaW5kcm9tZXMsIGNvdWxkIGVhc2lseSBnbyB1bmRldGVjdGVkIGlmIHRoZSByZWdpb25zIGV4YW1pbmVkIGFyZSB0b28gbGFyZ2UuIEFsc28sIGlmIHRoZSByZWdpb25zIGFyZSB0b28gc21hbGwsIGEgY2x1c3RlciBvZiBwYWxpbmRyb21lcyBtYXkgYmUgc3BsaXQgYmV0d2VlbiBhZGphY2VudCBpbnRlcmF2bHMgYW5kIG5vdCBhcHBlYXIgYXMgYSBoaWdoLWNvdW50IGludGVydmFsLgpgYGB7cn0KZmluYWwgPC0gYXJyYXkoZGltPWMoNTAwLDEpKQppbnRlcnZhbF9sZW5ndGggPC0gYXJyYXkoZGltPWMoNTAwLDEpKQpsYW1kYSA8LSBhcnJheShkaW09Yyg1MDAsMSkpCmZvciAoayBpbiAyMDoxMDApewogIHRhYiA8LSB0YWJsZShjdXQobG9jYXRpb25zLCBicmVha3MgPSBzZXEoMCwgTiwgbGVuZ3RoLm91dCA9IGsrMSksIGluY2x1ZGUubG93ZXN0ID0gVFJVRSkpCiAgaGVhZCh0YWIsMTApCiAgdGFiPC1hcy52ZWN0b3IodGFiKQogIGxhbWRhW2ssXSA8LXN1bSh0YWIpL2sKICB0aHJlc2hvbGQgPC1tYXgodGFiKQogIHJlc3VsdCA8LSAwCiAgaW50ZXJ2YWxfbGVuZ3RoW2ssXSA8LSBOL2sKICBmb3IgKGkgaW4gMDoodGhyZXNob2xkLTEpKXsKICAgIHJlc3VsdCA8LSByZXN1bHQrKChsYW1kYVtrXV5pKSpleHAoLWxhbWRhW2tdKS9mYWN0b3JpYWwoaSkpCiAgfQogIGZpbmFsW2ssXSA8LSAxLXJlc3VsdF5rCn0KcmVzdWx0IDwtIGRhdGEuZnJhbWUobGFtZGEsaW50ZXJ2YWxfbGVuZ3RoLGZpbmFsKQoKIyBEaXNwbGF5IFRhYmxlIGNvbnRhaW5pbmcgdGhlIHByb2JhYmlsaXR5IG9mIGEgUG9pc3NvbiBEaXN0cmlidXRpb24gaGF2aW5nIGUgZ3JlYXRlc3QgbnVtYmVyIG9mIGhpdHMgYXQgbGVhc3QgayBmb3IgZWFjaCBzdWItaW50ZXJ2YWwgZGl2aXNpb25zCnJlc3VsdFtjKDIwOjEwMCksXQpgYGAKCgojIyBBZGRpdGlvbmFsIFNjZW5hcmlvOiBUZXN0aW5nIGlmIEhJViBUZXN0aW5nIHBvc2l0aXZlIGlzIHJlbGF0ZWQgdG8gYWdlClRPRE8gRGVzY3JpcHRpb24KYGBge3J9CiMgQ2xlYW4gb3V0ICd1bmtub3duJyBkYXRhIGFuZCBjb252ZXJ0IGZhY3RvciB0byBudW1lcmljYWwgdmFsdWVzCmhlYWx0aCA8LSB0cmFuc2Zvcm0oaGVhbHRoLCBhZ2VfeXJzID0gYXMubnVtZXJpYyhhZ2VfeXJzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhpdiA9IGFzLmNoYXJhY3RlcihoaXYpKQpoZWFsdGguaW5kIDwtIHdoaWNoKGhlYWx0aCRoaXYgIT0gInVua25vd24iKQpoZWFsdGggPC0gaGVhbHRoW2hlYWx0aC5pbmQsXQoKIyBUb3RhbCBudW1iZXIgb2YgcGVvcGxlIHRoYXQgaGF2ZSBoaXYKcG9wdWxhdGlvbiA9IG5yb3coaGVhbHRoKQpwb3BfaGl2IDwtIG5yb3coaGVhbHRoW3doaWNoKGhlYWx0aCRoaXY9PSdwb3NpdGl2ZScpLF0pCgojIFNwbGl0IHRoZSBhZ2UgaW50byBmb3VyIGdyb3VwcwojIDAtMjAKYWdlX2ZpcnN0IDwtIGhlYWx0aCRhZ2VfeXJzW3doaWNoKGhlYWx0aCRhZ2VfeXJzPDIxKV0KYWdlX3Byb3BvcnRpb25fZmlyc3QgPC0gbGVuZ3RoKGFnZV9maXJzdCkvcG9wdWxhdGlvbgpoaXZfcHJvcG9ydGlvbl9maXJzdDwtIG5yb3coaGVhbHRoW3doaWNoKChoZWFsdGgkaGl2PT0gInBvc2l0aXZlIikgJiAoaGVhbHRoJGFnZV95cnMgPDIxKSksXSkvcG9wX2hpdgoKIyAyMS00MAphZ2Vfc2Vjb25kPC1oZWFsdGgkYWdlX3lyc1t3aGljaChoZWFsdGgkYWdlX3lycz4yMCAmIGhlYWx0aFsnYWdlX3lycyddPDQxKV0KYWdlX3Byb3BvcnRpb25fc2Vjb25kIDwtIGxlbmd0aChhZ2Vfc2Vjb25kKS9wb3B1bGF0aW9uCmhpdl9wcm9wb3J0aW9uX3NlY29uZDwtbnJvdyhoZWFsdGhbd2hpY2goaGVhbHRoJGFnZV95cnM+MjAgJmhlYWx0aCRhZ2VfeXJzPDQxICYgaGVhbHRoJGhpdj09InBvc2l0aXZlIiksXSkvcG9wX2hpdgoKIyA0MS02MAphZ2VfdGhpcmQ8LWhlYWx0aCRhZ2VfeXJzW3doaWNoKGhlYWx0aFsnYWdlX3lycyddPjQwICYgaGVhbHRoWydhZ2VfeXJzJ108NjEpXQphZ2VfcHJvcG9ydGlvbl90aGlyZCA8LSBsZW5ndGgoYWdlX3RoaXJkKS9wb3B1bGF0aW9uCmhpdl9wcm9wb3J0aW9uX3RoaXJkPC1ucm93KGhlYWx0aFt3aGljaChoZWFsdGgkYWdlX3lycz40MCAmIGhlYWx0aCRhZ2VfeXJzPDYxICZoZWFsdGgkaGl2PT0icG9zaXRpdmUiKSxdKS9wb3BfaGl2CgojIDYxKwphZ2VfbGFzdDwtaGVhbHRoJGFnZV95cnNbd2hpY2goaGVhbHRoWydhZ2VfeXJzJ10+NjApXQphZ2VfcHJvcG9ydGlvbl9sYXN0IDwtIGxlbmd0aChhZ2VfbGFzdCkvcG9wdWxhdGlvbgpoaXZfcHJvcG9ydGlvbl9sYXN0PC1ucm93KGhlYWx0aFt3aGljaChoZWFsdGgkYWdlX3lycz42MCAmIGhlYWx0aCRoaXY9PSJwb3NpdGl2ZSIpLF0pL3BvcF9oaXYKCiMgRXhwZWN0ZWQgRGF0YQpwb3B1bGF0aW9uX2Rpc3QgPC1jKGFnZV9wcm9wb3J0aW9uX2ZpcnN0LGFnZV9wcm9wb3J0aW9uX3NlY29uZCxhZ2VfcHJvcG9ydGlvbl90aGlyZCxhZ2VfcHJvcG9ydGlvbl9sYXN0KQojIE9ic2VydmVkIERhdGEKaGl2X2Rpc3Q8LWMoaGl2X3Byb3BvcnRpb25fZmlyc3QsaGl2X3Byb3BvcnRpb25fc2Vjb25kLGhpdl9wcm9wb3J0aW9uX3RoaXJkLGhpdl9wcm9wb3J0aW9uX2xhc3QpCgojIEdvb2RuZXNzLWZpdHRlc3QKY2hpXzIgPC0gc3VtKChoaXZfZGlzdCAtIHBvcHVsYXRpb25fZGlzdCleMi9wb3B1bGF0aW9uX2Rpc3QpCmNoaTJfY29tcGFyZSA8LSBxY2hpc3EocCA9IDAuOTUsIGRmID0gMykKcF92YWx1ZSA8LSBwY2hpc3EoY2hpXzIsIGRmID0gMywgbG93ZXIudGFpbCA9IEZBTFNFKQpwcmludChwYXN0ZSgiVGhlIHBfdmFsdWUgb2YgR29vZG5lc3Mgb2YgRml0IFRlc3QgaXMiLHBfdmFsdWUpKQoKI1Zpc3VhbGl6YXRpb24KUmVzaWR1YWxzIDwtIChoaXZfZGlzdCAtIHBvcHVsYXRpb25fZGlzdCkgLyBzcXJ0KHBvcHVsYXRpb25fZGlzdCkKcGxvdChSZXNpZHVhbHMsIHR5cGUgPSAnaCcsIHlsYWIgPSAiU3RhbmRhcmRpemVkIFJlc2lkdWFscyIsIHhsYWIgPSAiUHJvcG9ydGlvbiBvZiBQb3NpdGl2ZSBISVYiLCBtYWluID0gIlBsb3Qgb2YgU3RhbmRhcmRpemVkIFJlc2lkdWFsIGZvciBBZ2UgYW5kIEhJViBQb3NpdGl2ZSIpCmBgYAoKCk51bGwgSHlwb3RoZXNpczogVGhlIHByb3BvcnRpb24gb2YgYWdlIGluIHRoZSBwb3B1bGF0aW9uIGlzIHVucmVsYXRlZCB3aXRoIHRoZSBwcm9wb3J0aW9uIG9mIHBlb3BsZSBoYXZpbmcgaGl2LihBZ2UgaXMgbm90IGFuIGluZmx1ZW5jaW5nIGZhY3RvciBmb3IgSElWIHRlc3RpbmcgcG9zaXRpdmUpClNpbmNlIHAtdmFsdWUgb2YgdGhpcyBjaGktc3F1YXJlIGdvb2RuZXNzIG9mIGZpdCB0ZXN0IGlzIGNsb3NlIHRvIDEsIHdlIHNlZSB0aGF0IGRldmlhdGlvbnMgYXMgbGFyZ2UgYXMgb3VycyAob3IgbGFyZ2VyKSBhcmUgdmVyeSBsaWtlbHkuIEluIGFkZGl0aW9uLCBoYXZpbmcgdmFsdWVzIG9mIHRoZSBzdGFuZGFyZGl6ZWQgcmVzaWR1YWwgbGVzcyB0aGFuIDMgc3VnZ2VzdHMgdGhhdCBpdCBpcyBhIGdvb2QgZml0IG9mIHRoZSBhZ2UgZGlzdHJpYnV0aW9uIHRvIGVzdGltYXRlIHRoZSBwZW9wbGUgdGVzdGluZyBwb3NpdGl2ZSBvbiBoaXYuIEhlbmNlLCB3ZSByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcyBhbmQgY29uY2x1ZGUgdGhhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIHByb3BvcnRpb24gb2YgYWdlIG1hdGNoZXMgd2l0aCB0aGUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBwZW9wbGUgdGVzdGluZyBwb3NpdGl2ZSBvbiBISVYuCgo=